package net.jgrm.redisadvanced.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import net.jgrm.redisadvanced.config.RedisAdvancedProperties;
import net.jgrm.redisadvanced.service.RedisSetService;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Redis Set类型操作服务实现类
 */
@Slf4j
//@Service
public class RedisSetServiceImpl implements RedisSetService {

    //    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    //    @Autowired
    private RedisAdvancedProperties redisAdvancedProperties;

    //    @Autowired
    private ObjectMapper objectMapper;

    public RedisSetServiceImpl(RedisTemplate<String, Object> redisTemplate, RedisAdvancedProperties redisAdvancedProperties, ObjectMapper objectMapper) {
        this.redisTemplate = redisTemplate;
        this.redisAdvancedProperties = redisAdvancedProperties;
        this.objectMapper = objectMapper;
    }

    /**
     * 添加前缀到键名
     */
    private String addPrefix(String key) {
        return redisAdvancedProperties.getKeyPrefix() + key;
    }

    @Override
    public Long add(String key, Object... values) {
        try {
            Long result = redisTemplate.opsForSet().add(addPrefix(key), values);
            log.debug("Set add successful: key={}", key);
            return result;
        } catch (Exception e) {
            log.error("Set add error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis set add operation failed", e);
        }
    }

    @Override
    public <T> Set<T> members(String key, Class<T> clazz) {
        try {
            Set<Object> members = redisTemplate.opsForSet().members(addPrefix(key));
            log.debug("Set members successful: key={}", key);
            return convertSet(members, clazz);
        } catch (Exception e) {
            log.error("Set members error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis set members operation failed", e);
        }
    }

    @Override
    public Boolean isMember(String key, Object value) {
        try {
            Boolean result = redisTemplate.opsForSet().isMember(addPrefix(key), value);
            log.debug("Set isMember successful: key={}", key);
            return result;
        } catch (Exception e) {
            log.error("Set isMember error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis set isMember operation failed", e);
        }
    }

    @Override
    public Long size(String key) {
        try {
            Long result = redisTemplate.opsForSet().size(addPrefix(key));
            log.debug("Set size successful: key={}", key);
            return result;
        } catch (Exception e) {
            log.error("Set size error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis set size operation failed", e);
        }
    }

    @Override
    public Long remove(String key, Object... values) {
        try {
            Long result = redisTemplate.opsForSet().remove(addPrefix(key), values);
            log.debug("Set remove successful: key={}", key);
            return result;
        } catch (Exception e) {
            log.error("Set remove error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis set remove operation failed", e);
        }
    }

    @Override
    public <T> T pop(String key, Class<T> clazz) {
        try {
            Object value = redisTemplate.opsForSet().pop(addPrefix(key));
            log.debug("Set pop successful: key={}", key);
            return convertValue(value, clazz);
        } catch (Exception e) {
            log.error("Set pop error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis set pop operation failed", e);
        }
    }

    @Override
    public <T> T randomMember(String key, Class<T> clazz) {
        try {
            Object value = redisTemplate.opsForSet().randomMember(addPrefix(key));
            log.debug("Set randomMember successful: key={}", key);
            return convertValue(value, clazz);
        } catch (Exception e) {
            log.error("Set randomMember error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis set randomMember operation failed", e);
        }
    }

    @Override
    public <T> java.util.List<T> randomMembers(String key, long count, Class<T> clazz) {
        try {
            List<Object> values = redisTemplate.opsForSet().randomMembers(addPrefix(key), count);
            log.debug("Set randomMembers successful: key={}, count={}", key, count);
            return convertList(values, clazz);
        } catch (Exception e) {
            log.error("Set randomMembers error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis set randomMembers operation failed", e);
        }
    }

    @Override
    public <T> Set<T> intersect(String key1, String key2, Class<T> clazz) {
        try {
            Set<Object> intersect = redisTemplate.opsForSet().intersect(addPrefix(key1), addPrefix(key2));
            log.debug("Set intersect successful: key1={}, key2={}", key1, key2);
            return convertSet(intersect, clazz);
        } catch (Exception e) {
            log.error("Set intersect error: key1={}, key2={}, error={}", key1, key2, e.getMessage(), e);
            throw new RuntimeException("Redis set intersect operation failed", e);
        }
    }

    @Override
    public <T> Set<T> intersect(String key, String[] otherKeys, Class<T> clazz) {
        try {
            Collection<String> keys = new ArrayList<>(otherKeys.length + 1);
            keys.add(addPrefix(key));
            for (String otherKey : otherKeys) {
                keys.add(addPrefix(otherKey));
            }

            Set<Object> intersect = redisTemplate.opsForSet().intersect(keys);
            log.debug("Set intersect multiple successful: key={}", key);
            return convertSet(intersect, clazz);
        } catch (Exception e) {
            log.error("Set intersect multiple error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis set intersect multiple operation failed", e);
        }
    }

    @Override
    public Long intersectAndStore(String key1, String key2, String destKey) {
        try {
            Long result = redisTemplate.opsForSet().intersectAndStore(addPrefix(key1), addPrefix(key2), addPrefix(destKey));
            log.debug("Set intersectAndStore successful: key1={}, key2={}, destKey={}", key1, key2, destKey);
            return result;
        } catch (Exception e) {
            log.error("Set intersectAndStore error: key1={}, key2={}, destKey={}, error={}", key1, key2, destKey, e.getMessage(), e);
            throw new RuntimeException("Redis set intersectAndStore operation failed", e);
        }
    }

    @Override
    public Long intersectAndStore(String key, String[] otherKeys, String destKey) {
        try {
            Collection<String> keys = new ArrayList<>(otherKeys.length + 1);
            keys.add(addPrefix(key));
            for (String otherKey : otherKeys) {
                keys.add(addPrefix(otherKey));
            }

            Long result = redisTemplate.opsForSet().intersectAndStore(keys, addPrefix(destKey));
            log.debug("Set intersectAndStore multiple successful: key={}, destKey={}", key, destKey);
            return result;
        } catch (Exception e) {
            log.error("Set intersectAndStore multiple error: key={}, destKey={}, error={}", key, destKey, e.getMessage(), e);
            throw new RuntimeException("Redis set intersectAndStore multiple operation failed", e);
        }
    }

    @Override
    public <T> Set<T> union(String key1, String key2, Class<T> clazz) {
        try {
            Set<Object> union = redisTemplate.opsForSet().union(addPrefix(key1), addPrefix(key2));
            log.debug("Set union successful: key1={}, key2={}", key1, key2);
            return convertSet(union, clazz);
        } catch (Exception e) {
            log.error("Set union error: key1={}, key2={}, error={}", key1, key2, e.getMessage(), e);
            throw new RuntimeException("Redis set union operation failed", e);
        }
    }

    @Override
    public <T> Set<T> union(String key, String[] otherKeys, Class<T> clazz) {
        try {
            Collection<String> keys = new ArrayList<>(otherKeys.length + 1);
            keys.add(addPrefix(key));
            for (String otherKey : otherKeys) {
                keys.add(addPrefix(otherKey));
            }

            Set<Object> union = redisTemplate.opsForSet().union(keys);
            log.debug("Set union multiple successful: key={}", key);
            return convertSet(union, clazz);
        } catch (Exception e) {
            log.error("Set union multiple error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis set union multiple operation failed", e);
        }
    }

    @Override
    public Long unionAndStore(String key1, String key2, String destKey) {
        try {
            Long result = redisTemplate.opsForSet().unionAndStore(addPrefix(key1), addPrefix(key2), addPrefix(destKey));
            log.debug("Set unionAndStore successful: key1={}, key2={}, destKey={}", key1, key2, destKey);
            return result;
        } catch (Exception e) {
            log.error("Set unionAndStore error: key1={}, key2={}, destKey={}, error={}", key1, key2, destKey, e.getMessage(), e);
            throw new RuntimeException("Redis set unionAndStore operation failed", e);
        }
    }

    @Override
    public Long unionAndStore(String key, String[] otherKeys, String destKey) {
        try {
            Collection<String> keys = new ArrayList<>(otherKeys.length + 1);
            keys.add(addPrefix(key));
            for (String otherKey : otherKeys) {
                keys.add(addPrefix(otherKey));
            }

            Long result = redisTemplate.opsForSet().unionAndStore(keys, addPrefix(destKey));
            log.debug("Set unionAndStore multiple successful: key={}, destKey={}", key, destKey);
            return result;
        } catch (Exception e) {
            log.error("Set unionAndStore multiple error: key={}, destKey={}, error={}", key, destKey, e.getMessage(), e);
            throw new RuntimeException("Redis set unionAndStore multiple operation failed", e);
        }
    }

    @Override
    public <T> Set<T> difference(String key1, String key2, Class<T> clazz) {
        try {
            Set<Object> difference = redisTemplate.opsForSet().difference(addPrefix(key1), addPrefix(key2));
            log.debug("Set difference successful: key1={}, key2={}", key1, key2);
            return convertSet(difference, clazz);
        } catch (Exception e) {
            log.error("Set difference error: key1={}, key2={}, error={}", key1, key2, e.getMessage(), e);
            throw new RuntimeException("Redis set difference operation failed", e);
        }
    }

    @Override
    public <T> Set<T> difference(String key, String[] otherKeys, Class<T> clazz) {
        try {
            Collection<String> keys = new ArrayList<>(otherKeys.length);
            for (String otherKey : otherKeys) {
                keys.add(addPrefix(otherKey));
            }

            Set<Object> difference = redisTemplate.opsForSet().difference(addPrefix(key), keys);
            log.debug("Set difference multiple successful: key={}", key);
            return convertSet(difference, clazz);
        } catch (Exception e) {
            log.error("Set difference multiple error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis set difference multiple operation failed", e);
        }
    }

    @Override
    public Long differenceAndStore(String key1, String key2, String destKey) {
        try {
            Long result = redisTemplate.opsForSet().differenceAndStore(addPrefix(key1), addPrefix(key2), addPrefix(destKey));
            log.debug("Set differenceAndStore successful: key1={}, key2={}, destKey={}", key1, key2, destKey);
            return result;
        } catch (Exception e) {
            log.error("Set differenceAndStore error: key1={}, key2={}, destKey={}, error={}", key1, key2, destKey, e.getMessage(), e);
            throw new RuntimeException("Redis set differenceAndStore operation failed", e);
        }
    }

    @Override
    public Long differenceAndStore(String key, String[] otherKeys, String destKey) {
        try {
            Collection<String> keys = new ArrayList<>(otherKeys.length);
            for (String otherKey : otherKeys) {
                keys.add(addPrefix(otherKey));
            }

            Long result = redisTemplate.opsForSet().differenceAndStore(addPrefix(key), keys, addPrefix(destKey));
            log.debug("Set differenceAndStore multiple successful: key={}, destKey={}", key, destKey);
            return result;
        } catch (Exception e) {
            log.error("Set differenceAndStore multiple error: key={}, destKey={}, error={}", key, destKey, e.getMessage(), e);
            throw new RuntimeException("Redis set differenceAndStore multiple operation failed", e);
        }
    }

    @Override
    public Boolean expire(String key, long expireTime, TimeUnit timeUnit) {
        try {
            Boolean result = redisTemplate.expire(addPrefix(key), expireTime, timeUnit);
            log.debug("Set expire successful: key={}, expireTime={}, timeUnit={}", key, expireTime, timeUnit);
            return result;
        } catch (Exception e) {
            log.error("Set expire error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis set expire operation failed", e);
        }
    }

    /**
     * 将单个对象转换为指定类型
     */
    private <T> T convertValue(Object value, Class<T> clazz) {
        if (value == null) {
            return null;
        }
        if (clazz.isInstance(value)) {
            return clazz.cast(value);
        } else {
            return objectMapper.convertValue(value, clazz);
        }
    }

    /**
     * 将对象列表转换为指定类型的列表
     */
    private <T> List<T> convertList(List<Object> values, Class<T> clazz) {
        List<T> result = new ArrayList<>(values.size());
        for (Object value : values) {
            result.add(convertValue(value, clazz));
        }
        return result;
    }

    /**
     * 将对象Set转换为指定类型的Set
     */
    private <T> Set<T> convertSet(Set<Object> values, Class<T> clazz) {
        Set<T> result = new HashSet<>(values.size());
        for (Object value : values) {
            result.add(convertValue(value, clazz));
        }
        return result;
    }
}